package com.sojson.project.sys.role.service.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sojson.config.mybatis.annotation.DataScope;
import com.sojson.constant.Constant;
import com.sojson.constant.ConstantDict;
import com.sojson.constant.ConstantField;
import com.sojson.enums.EEnvironmentUserType;
import com.sojson.enums.EUserType;
import com.sojson.project.sys.dict.entity.vo.DictDataVo;
import com.sojson.project.sys.menu.dao.MenuDao;
import com.sojson.project.sys.menu.service.MenuService;
import com.sojson.project.sys.role.dao.RoleDao;
import com.sojson.project.sys.role.dao.RoleMenuDao;
import com.sojson.project.sys.role.dao.UserRoleDao;
import com.sojson.project.sys.role.entity.dto.RoleDto;
import com.sojson.project.sys.role.entity.dto.RolePageDto;
import com.sojson.project.sys.role.entity.po.Role;
import com.sojson.project.sys.role.entity.po.RoleMenu;
import com.sojson.project.sys.role.entity.po.UserRole;
import com.sojson.project.sys.role.entity.vo.RoleVo;
import com.sojson.project.sys.role.service.RoleMenuService;
import com.sojson.project.sys.role.service.RoleService;
import com.sojson.util.DictUtil;
import com.sojson.util.ExpUtil;
import com.sojson.util.RoleUtil;
import com.sojson.util.param.ParamUtil;
import com.sojson.util.token.Token;
import com.sojson.util.token.TokenUtil;

/**
 * 角色信息表Service实现类
 * 
 * @author liu
 * @date 2021-04-13
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleDao, Role> implements RoleService {

    @Lazy
    @Autowired
    private RoleServiceImpl roleService;
    @Autowired
    private RoleMenuService roleMenuService;
    @Autowired
    private MenuService menuService;
    @Resource
    private RoleDao roleDao;
    @Resource
    private MenuDao menuDao;
    @Resource
    private RoleMenuDao roleMenuDao;
    @Resource
    private UserRoleDao userRoleDao;

    /**
     * 单条插入
     * 
     * @param dto
     * @return
     * @throws IOException 
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(Role dto) throws IOException {
        // 去重
        LambdaQueryWrapper<Role> query = Wrappers.lambdaQuery(Role.class);
        query.eq(Role::getKey, dto.getKey());
        int count = roleDao.selectCount(query);
        if (count > 0) {
            ExpUtil.throwEx("Key已存在");
        }

        // 添加角色
        roleDao.insert(dto);

        // 给当前用户添加此角色
        if (!ParamUtil.isAdminSuper()) {
            UserRole userRole = new UserRole();
            userRole.setUserId(TokenUtil.getUserId());
            userRole.setRoleId(dto.getId());
            userRoleDao.insert(userRole);
        }

        return roleService.insertRoleMenu(dto);
    }

    /**
     * 通过id更新
     * 
     * @param dto
     * @return
     * @throws IOException 
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(Role dto) throws IOException {
        Role selectById = roleDao.selectById(dto.getId());
        if (selectById == null) {
            return true;
        }

        // 校验角色是否允许操作
        checkRoleAllowed(selectById);

        // 修改角色
        dto.setEnvironment(selectById.getEnvironment());
        int updatePartialById = roleDao.updateById(dto);
        if (updatePartialById < 1) {
            return false;
        }

        // 添加角色与菜单关联
        roleService.insertRoleMenu(dto);
        return true;
    }

    /**
     * 新增角色菜单信息
     *
     * @param role 角色对象
     * @throws IOException 
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean insertRoleMenu(Role dto) throws IOException {
        String id = dto.getId();
        Set<String> menuIds = dto.getMenuIds();
        if (menuIds != null && menuIds.size() > 1) {
            // 判断是否有此菜单权限
            Set<String> permIdsExist = TokenUtil.getPermIdsExist();
            Set<String> loginMenuIds = new HashSet<>(permIdsExist);
            // 添加环境菜单
            List<DictDataVo> gets = DictUtil.gets(ConstantDict.ENV_USER_TYPE);
            for (DictDataVo dictData : gets) {
                loginMenuIds.add(dictData.getVal());
            }
            ParamUtil.verifyPerm(loginMenuIds, dto.getMenuIds());

            // 如果不是管理员,判断权限环境是否匹配
            boolean isAdmin = false;
            String environment = dto.getEnvironment();
            EEnvironmentUserType[] values = EEnvironmentUserType.values();
            for (EEnvironmentUserType env : values) {
                if (env.getCode().equals(environment)) {
                    if (EUserType.ADMIN.getCode().equals(env.getType())) {
                        isAdmin = true;
                    }
                    break;
                }
            }
            if (!isAdmin) {
                int countByNotEnv = roleMenuDao.countByNotEnv(dto);
                if (countByNotEnv > 0) {
                    ExpUtil.throwEx("菜单环境不匹配!");
                }
            }

            // 删除角色与菜单关联
            roleMenuDao.deleteByRoleId(id);

            // 新增角色与菜单管理
            List<RoleMenu> list = new ArrayList<>();
            for (String menuId : menuIds) {
                RoleMenu rm = new RoleMenu();
                rm.setRoleId(id);
                rm.setMenuId(menuId);
                list.add(rm);
            }
            if (list.size() > 0) {
                roleMenuService.saveBatch(list);
            }
        }
        return true;
    }

    /**
     * 列表查询
     * 
     * @param dto
     * @return
     */
    @Override
    @DataScope(roleIsAll = true, roleAlias = "r", roleField = ConstantField.ID)
    public List<RoleVo> list(RoleDto dto) {
        List<RoleVo> list = roleDao.list(dto);
        return list;
    }

    /**
     * 分页查询
     * 
     * @param dto
     * @return
     */
    @Override
    @DataScope(roleIsAll = true, roleAlias = "r", roleField = ConstantField.ID)
    public List<RoleVo> page(RolePageDto dto) {
        List<RoleVo> page = roleDao.page(dto);
        return page;
    }

    /**
     * 通过id查询
     * 
     * @param id
     * @return
     * @throws IOException 
     */
    @Override
    public RoleVo findById(String id) throws IOException {
        return roleDao.findById(id);
    }

    /**
     * 通过对象删除
     * 
     * @param dto
     * @return
     */
    @Override
    public boolean removeByDto(RoleDto dto) {
        roleDao.removeByDto(dto);
        return true;
    }

    /**
     * 清空数据
     * 
     * @return
     */
    @Override
    public boolean clear() {
        roleDao.clear();
        return true;
    }

    /**
     * 通过id列表删除
     * 
     * @param ids
     * @return
     * @throws IOException 
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByIds(Set<String> ids) throws IOException {
        checkRoleAllowed(ids);
        if (countUserRoleByRoleIds(ids) > 0) {
            ExpUtil.throwEx("角色已分配,不能删除");
        }

        // 删除角色关联的菜单
        roleMenuDao.deleteByRoleIds(ids);

        // 删除角色
        roleDao.deleteBatchIds(ids);

        // 删除用户关联的角色
        userRoleDao.deleteByRoleIds(ids);
        return true;
    }

    /**
     * 状态修改
     * 
     * @param dto
     * @return
     * @throws IOException 
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateStatus(Role dto) throws IOException {
        String id = dto.getId();

        // 校验角色是否允许操作
        checkRoleAllowed(roleDao.selectById(id));

        // 修改角色
        roleDao.updateById(dto);
        return true;
    }

    /**
     * 校验角色是否允许操作
     *
     * @param ids 角色ID集合
     */
    @Override
    public void checkRoleAllowed(Set<String> ids) {
        if (menuService.getAdminSuperRoleNumByRoleIds(ids) > 0) {
            ExpUtil.throwEx("不允许操作超级管理员角色");
        }
    }

    /**
     * 校验角色是否允许操作
     *
     * @param dto 角色信息
     */
    @Override
    public void checkRoleAllowed(Role dto) {
        if (ParamUtil.isAdminSuper(dto.getKey())) {
            ExpUtil.throwEx("不允许操作超级管理员角色");
        }
    }

    /**
     * 修改保存数据权限
     * 
     * @param dto
     * @return
     * @throws IOException 
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean authDataScope(Role dto) throws IOException {
        // 校验角色是否允许操作
        checkRoleAllowed(dto);

        // 修改角色
        roleDao.updateById(dto);
        return true;
    }

    /**
     * 通过角色ID集合查询角色使用数量
     *
     * @param roleIds 角色ID集合
     * @return 结果
     */
    @Override
    public int countUserRoleByRoleIds(Set<String> roleIds) {
        return userRoleDao.countUserRoleByRoleIds(Constant.USET_TABLE_NAMES, roleIds);
    }

    /**
     * 根据用户ID获取角色列表
     * 
     * @param userId
     * @return
     */
    @Override
    public List<RoleVo> getRolesByUserId(String userId) {
        return roleDao.getRolesByUserId(userId);
    }

    /**
     * 根据用户ID获取角色ID列表
     * 
     * @param userId
     * @return
     */
    @Override
    public Set<String> getRoleIdsByUserId(String userId) {
        return roleDao.getRoleIdsByUserId(userId);
    }

    /**
     * 根据用户ID和环境获取角色ID列表
     * 
     * @param userId
     * @param env
     * @return
     */
    @Override
    public Set<String> getRoleIdsByUserIdAndEnv(String userId, String env) {
        return roleDao.getRoleIdsByUserIdAndEnv(userId, env);
    }

    /**
     * 获取权限组
     * 
     * @return
     */
    @Override
    public List<RoleVo> getPerms() {
        return roleDao.getPerms();
    }

    /**
     * 统计不是此环境的角色数量
     * 
     * @param envs
     * @param roleIds
     * @return
     */
    @Override
    public int countByNotEnv(List<String> envs, Set<String> roleIds) {
        return roleDao.countByNotEnv(envs, roleIds);
    }

    /**
     * 刷新权限
     * 
     * @return
     * @throws IOException 
     */
    @Override
    public void refreshPerms() throws IOException {
        // 刷新权限
        RoleUtil.refreshPerms();

        // 刷新本人权限
        Token token = TokenUtil.getToken();
        token.setRoleIds(roleService.getRoleIdsByUserIdAndEnv(token.getUser().getId(), token.getLoginEnv()));
        TokenUtil.refreshToken(token);
    }

}